﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Docker.DotNet;
using Docker.DotNet.BasicAuth;
using Docker.DotNet.Models;
using Grpc.Core;
using KubeClient;
using KubeClient.Models;
using LibGit2Sharp;
using Microsoft.Extensions.Configuration;

namespace CodeCity.K8S.Service.Services
{
    public class UserService : DockerService.DockerServiceBase
    {
        private static string registry = Environment.GetEnvironmentVariable("REGISTRY") ?? "registry.cn-shanghai.aliyuncs.com";
        private static string deamonUrl = Environment.GetEnvironmentVariable("DEAMONURL") ?? "tcp://localhost:2375";
        private static string username = Environment.GetEnvironmentVariable("REGISTRY_USERNAME") ?? "1101385391@qq.com";
        private static string password = Environment.GetEnvironmentVariable("REGISTRY_PASSWORD") ?? "codecity123";
        private static string userNamespace = Environment.GetEnvironmentVariable("USER_NAMESPACE") ?? "codecity";
        public static string templateImage = Environment.GetEnvironmentVariable("TEMPLATE_IMAGE") ?? $"{registry}/codecity/nginx";
        public static string templateImageWithTag = Environment.GetEnvironmentVariable("TEMPLATE_IMAGE_WITHTAG") ?? $"{templateImage}:latest";
        private static string k8sConfigFile = Environment.GetEnvironmentVariable("K8SCONFIGFILE") ?? @"C:\Users\wzxin\.kube\config";
        private static string kubeContextName = Environment.GetEnvironmentVariable("KUBECONTEXTNAME") ?? @"docker-desktop";
        private static BasicAuthCredentials credentials = new BasicAuthCredentials(username, password);
        private static string loadBalancerId = Environment.GetEnvironmentVariable("LOADBALANCERID") ?? "lb-uf6gtmhishceu658flynw";
        private static string k8sNamespace = "codecity-user";
        private readonly IConfiguration configuration;

        public UserService(IConfiguration configuration)
        {
            this.configuration = configuration;
        }
        public static async Task<string> RunExeAsync(string exePath, string arguments)
        {

            using Process process = new System.Diagnostics.Process();
            process.StartInfo.FileName = exePath;
            process.StartInfo.Arguments = arguments;
            // 必须禁用操作系统外壳程序  
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.RedirectStandardOutput = true;

            process.Start();

            var result = process.StandardOutput.ReadToEnd();

            await process.WaitForExitAsync();
            process.Close();
            return result;
        }

        

        public override async Task<RunResponse> Run(RunRequest request, ServerCallContext context)
        {
            var limitConfig = configuration["UserDockerLimit"];
            var envs = request.Envs.Select(x => $"-e {Replace(x.Key)}={Replace(x.Value)}");
            var env = string.Join(" ", envs);
            await Process.Start("docker", $"rm -f {request.Name}").WaitForExitAsync();
            await Process.Start("docker", $"run -d {limitConfig} -P  --restart=always --name { request.Name} {env}").WaitForExitAsync();
            var runResult = await RunExeAsync("docker", $"inspect {request.Name}");
            var port = Regex.Match(runResult, @":80-\>(\d+)").Groups[1].Value;
            return new RunResponse()
            {
                Port = Convert.ToInt32(port)
            };
        }

        private static string Replace(string str)
        {
            return str.Replace(" ", "_")
                .Replace("\n", "_")
                .Replace("\r\n", "_")
                .Replace("\t", "_");
        }

        public override async Task<FetchResponse> Fetch(FetchRequest request, ServerCallContext context)
        {
            var path = Path.Combine(configuration["UserClonePath"], Path.GetFileNameWithoutExtension(request.Repository));
            if (Directory.Exists(path))
            {
                DeleteReadOnly(new DirectoryInfo(path));
                if (Directory.Exists(path))
                {
                    Directory.Delete(path, true);
                }
            }
            string gitCommand = "git";
            string gitAddArgument = @"add -A";
            string gitCommitArgument = @"commit ""explanations_of_changes""";
            string gitPushArgument = @"push our_remote";

            await Process.Start(gitCommand, $"clone {request.Repository} {path}").WaitForExitAsync();
            //Repository.Clone(request.Repository, path);
            //var folders = Directory.EnumerateDirectories(path);
            //foreach (var folder in folders)
            //{
            //    if (folder.Contains("_git2_"))
            //    {
            //        DeleteReadOnly(new DirectoryInfo(folder));
            //        break;
            //    }
            //}
            return new FetchResponse();
        }
        public void DeleteReadOnly(FileSystemInfo fileSystemInfo)
        {
            var directoryInfo = fileSystemInfo as DirectoryInfo;
            if (directoryInfo != null)
            {
                foreach (FileSystemInfo childInfo in directoryInfo.GetFileSystemInfos())
                {
                    DeleteReadOnly(childInfo);
                }
            }

            fileSystemInfo.Attributes = FileAttributes.Normal;
            fileSystemInfo.Delete();
        }

        public override async Task<BuildResponse> Build(BuildRequest request, ServerCallContext context)
        {
            var path = Path.Combine(configuration["UserClonePath"], Path.GetFileNameWithoutExtension(request.Repository), "src", "CityManagementSystem", "Dockerfile");
            if (File.Exists(path))
            {
                File.Delete(path);
            }
            Process.Start("docker", "rm -f " + request.ImageName);
            File.Copy("UserDockerfile", path);
            var processStartInfo = new ProcessStartInfo()
            {
                WorkingDirectory = Path.GetDirectoryName(path),
                FileName = "docker",
                Arguments = $"build -t {request.ImageName} ."
            };
            await Process.Start(processStartInfo).WaitForExitAsync();
            return new BuildResponse();
        }

        public override async Task<PushResponse> Push(PushRequest request, ServerCallContext context)
        {
            var registry = configuration["DockerRegistry"];
            await Process.Start("docker", $"tag {request.Image} {registry}/{request.Image}").WaitForExitAsync();
            await Process.Start("docker", $"push {registry}/{request.Image}").WaitForExitAsync();
            return new PushResponse();
        }



        public override async Task<RegisterImageResponse> RegisterImage(RegisterImageRequest request, ServerCallContext context)
        {
            if (string.IsNullOrWhiteSpace(request.Username))
            {
                context.Status = new Status(StatusCode.InvalidArgument, "用户名为空");
                return new RegisterImageResponse();
            }
            var client = new DockerClientConfiguration(
               new Uri(deamonUrl),
               credentials)
              .CreateClient();

            var imageName = $"{userNamespace}/{request.Username}";
            try
            {
                await client.Images.DeleteImageAsync(imageName + ":latest", new ImageDeleteParameters()
                {
                    Force = true,
                    PruneChildren = true
                });
            }
            catch (DockerImageNotFoundException)
            {
            }

            //pull image
            try
            {
                await client.Images.CreateImageAsync(new ImagesCreateParameters()
                {
                    FromImage = imageName
                }, new AuthConfig()
                {
                    Email = username,
                    Password = password
                }, new CreateImageProgress());
            }
            catch (DockerApiException ae) when (ae.StatusCode == System.Net.HttpStatusCode.NotFound)
            {
                try
                {
                    await client.Images.DeleteImageAsync(templateImageWithTag, new ImageDeleteParameters()
                    {
                        Force = true,
                        PruneChildren = true
                    });
                }
                catch (DockerImageNotFoundException)
                {

                }
                await client.Images.CreateImageAsync(new ImagesCreateParameters()
                {
                    FromImage = templateImageWithTag
                }, new AuthConfig()
                {
                    Email = username,
                    Password = password
                }, new CreateImageProgress());
                await client.Images.TagImageAsync(templateImageWithTag, new ImageTagParameters()
                {
                    RepositoryName = $"{registry}/{userNamespace}/{request.Username}",
                    Force = true,
                    Tag = "latest"
                });
                await client.Images.PushImageAsync($"{registry}/{userNamespace}/{request.Username}", new ImagePushParameters()
                {
                    Tag = "latest",
                    RegistryAuth = new AuthConfig()
                    {
                        Username = username,
                        Email = username,
                        Password = password
                    }
                }, new AuthConfig()
                {
                    Username = username,
                    Email = username,
                    Password = password
                }, new CreateImageProgress());
            }
            catch (DockerImageNotFoundException)
            {
                await client.Images.CreateImageAsync(new ImagesCreateParameters()
                {
                    FromImage = templateImageWithTag,
                    Repo = registry,
                    Tag = "latest"
                }, new AuthConfig()
                {
                    Email = username,
                    Password = password
                }, null);
                await client.Images.TagImageAsync(templateImageWithTag, new ImageTagParameters()
                {
                    Force = true,
                    Tag = $"user/{request.Username}:latest"
                });
            }
            return new RegisterImageResponse();
        }

        public override async Task<DeployFromGiteeResponse> DeployFromGitee(DeployFromGiteeRequest request, ServerCallContext context)
        {
            //debug
            Console.WriteLine("request.Token");
            Console.WriteLine(request.Token);
            Console.WriteLine("request.Username");
            Console.WriteLine(request.Username);
            Console.WriteLine("request.Password");
            Console.WriteLine(request.Password);

            //var menus = await this.DeployFromGitee(request.Token, request.Username, request.Password);

            var result = new DeployFromGiteeResponse();
            //result.Menus.AddRange(menus);
            return result;

        }

        private static async Task CreateDeploymentAsync(DeployRequest request, KubeApiClient client)
        {
            var podSpecV1 = new KubeClient.Models.PodSpecV1();
            var container = new KubeClient.Models.ContainerV1()
            {
                Image = $"{registry}/{userNamespace}/{request.Username}",
                Name = request.Username
            };
            container.Ports.Add(new KubeClient.Models.ContainerPortV1()
            {
                ContainerPort = 80,
                Name = request.Username
            });
            container.Resources = new ResourceRequirementsV1();
            container.Resources.Limits.Add("cpu", "100m");//每个用户占用十分之一的单核资源
            container.Resources.Limits.Add("memory", "100Mi");
            podSpecV1.Containers.Add(container);
            var podMetaData = new KubeClient.Models.ObjectMetaV1()
            {
                Name = $"{request.Username}",
                Namespace = k8sNamespace,
            };
            podMetaData.Labels.Add("app", request.Username);
            var podTemplateV1 = new KubeClient.Models.PodTemplateSpecV1()
            {
                Metadata = podMetaData,
                Spec = podSpecV1
            };
            var deploymentsMetadata = new KubeClient.Models.ObjectMetaV1()
            {
                Name = request.Username,
                Namespace = k8sNamespace,
            };
            deploymentsMetadata.Labels.Add("app", request.Username);
            var deploySepc = new KubeClient.Models.DeploymentSpecV1()
            {
                Template = podTemplateV1,
                Selector = new KubeClient.Models.LabelSelectorV1()
            };
            var selector = new KubeClient.Models.LabelSelectorRequirementV1()
            {
                Key = "app",
                Operator = "In"
            };
            selector.Values.Add(request.Username);
            deploySepc.Selector.MatchExpressions.Add(selector);
            var deployment = await client.DeploymentsV1().Create(new KubeClient.Models.DeploymentV1()
            {
                Metadata = deploymentsMetadata,
                Spec = deploySepc
            });
        }

        private static async Task CreateServiceAsync(DeployRequest request, KubeApiClient client)
        {
            var serviceSpec = new KubeClient.Models.ServiceSpecV1();
            serviceSpec.Selector.Add("app", request.Username);
            serviceSpec.ExternalTrafficPolicy = "Local";
            serviceSpec.Ports.Add(new KubeClient.Models.ServicePortV1()
            {
                Port = request.Port,
                TargetPort = 80,
                Name = request.Username
            });
            serviceSpec.Type = "LoadBalancer";
            var serviceMetadata = new ObjectMetaV1()
            {
                Name = request.Username,
                Namespace = k8sNamespace
            };
            serviceMetadata.Annotations.Add("service.beta.kubernetes.io/alicloud-loadbalancer-id", loadBalancerId);
            serviceMetadata.Annotations.Add("service.beta.kubernetes.io/alicloud-loadbalancer-force-override-listeners", "true");
            var service = await client.ServicesV1().Create(new KubeClient.Models.ServiceV1()
            {
                Metadata = serviceMetadata,
                Spec = serviceSpec
            });

            Console.WriteLine(service.Status.LoadBalancer.Ingress.Count);
        }

        //private async Task<List<MenuItemDTO>> DeployFromGitee(string mayunToken, string userName, string pwd)
        //{
        //    var builder = new BuildImageHelper(mayunToken);
        //    var project = await builder.GitClone();
        //    Console.WriteLine($"clone Success {project}");


        //    await builder.InitUserAppSettings(project, userName, pwd);
        //    Console.WriteLine($"InitUserAppSettings Success");

        //    var result = await builder.GetMenus(project, "CodeCityMenu");
        //    Console.WriteLine("get menu success");

        //    foreach (var item in result)
        //    {
        //        Console.WriteLine($"{item.Title} {item.Url} {item.Type}");
        //    }


        //    var imageName = $"{registry}/{userNamespace}/{userName}:latest";
        //    builder.DockerBuild(project, imageName);
        //    Console.WriteLine($"DockerBuild Success {imageName}");




        //    var client = new DockerClientConfiguration(
        //       new Uri(deamonUrl),
        //       credentials)
        //      .CreateClient();



        //    await client.Images.PushImageAsync(imageName, new ImagePushParameters()
        //    {
        //        Tag = "latest",
        //        RegistryAuth = new AuthConfig()
        //        {
        //            Username = username,
        //            Email = username,
        //            Password = password
        //        }
        //    }, new AuthConfig()
        //    {
        //        Username = username,
        //        Email = username,
        //        Password = password
        //    }, new CreateImageProgress());

        //    Console.WriteLine("publish image success");



        //    return result;
        //}
    }
}
